package spl.army;

import java.security.acl.LastOwnerException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Formatter;
import java.util.Iterator;
import java.util.List;

import spl.utils.GlobalLogger;

public class Mission {
	private String missionName;
	private String requiredSkill;
	private int timeLeft;
	private List<ItemRequired> itemsRequired;
	private List<Mission> preReqMissions;
	private Sergeant assignedSergeant;
	private boolean isActive;
	
	
	/**
	 * @param missionNameStr
	 * @param requiredSkillStr
	 * @param nTimeLeft
	 * @param itemsRequiredLst
	 * @param preReqMissionsLst
	 */
	private void initMission(String missionNameStr, String requiredSkillStr, int nTimeLeft,
			List<ItemRequired> itemsRequiredLst, List<Mission> preReqMissionsLst) {
		this.missionName = missionNameStr;
		this.requiredSkill = requiredSkillStr;
		this.timeLeft = nTimeLeft;
		this.itemsRequired = itemsRequiredLst;
		this.preReqMissions = preReqMissionsLst;
		this.assignedSergeant = null;
		this.isActive = false;
		
		
		GlobalLogger.getLogger().finest("Mission " + this.missionName + " was created!");
	}

	/**
	 * Constructs Mission Instance
	 * 
	 * @param missionNameStr Name of mission
	 * @param requiredSkillStr Skill required to perform the mission
	 * @param nTimeLeft amount of time of the mission
	 * @param itemsRequiredLst items required
	 * @param preReqMissionsLst missions that should be done beforehand
	 */
	public Mission(String missionNameStr, String requiredSkillStr, int nTimeLeft,
			List<ItemRequired> itemsRequiredLst, List<Mission> preReqMissionsLst) {
		this.initMission(missionNameStr, requiredSkillStr, nTimeLeft, itemsRequiredLst, preReqMissionsLst);
	}

	/**
	 * @param name  Name of mission
	 * @param skill Skill required to perform the mission
	 * @param timeStr amount of time of the mission
	 * @param itemsStr items required as "item1, item2,..." or "item1 item2..."
	 * @param preRequsies missions that should be done beforehand
	 */
	public Mission(String name, String skill, String timeStr,
			String itemsStr, List<Mission> preRequsies) {

		// Hours needed
		int hoursNeeded = Integer.decode(timeStr.trim());
		
		// Items needed
		ArrayList<String> itemsRaw = new ArrayList<String>(
				Arrays.asList(itemsStr.split("[,\\s]+")) );
		
		ArrayList<ItemRequired> itemsRequiredLst = new ArrayList<ItemRequired>();
		for(int j=0;j<itemsRaw.size();j+=2) {
			String currentItemName = itemsRaw.get(j);
			int currentItemAmount = Integer.decode(itemsRaw.get(j+1).trim());
			itemsRequiredLst.add(new ItemRequired(currentItemName, currentItemAmount));
		}
		
		this.initMission(name, skill, hoursNeeded, itemsRequiredLst, preRequsies);
	}

	/**
	 * Assign a sergeant to the mission
	 * @param s is the sergeant that will have the mission
	 * @return false if sergeant s was already assigned. true otherwise.
	 * @throws LastOwnerException when the mission is not assigned.
	 */
	public boolean assignSergeant(Sergeant s) throws LastOwnerException{
		// Check if no one is trying to assign already signed assignment :)
		if(this.assignedSergeant != null & this.assignedSergeant != s) {
			throw new LastOwnerException();
		}
		
		boolean wasntAssigned = this.assignedSergeant == null; 

		this.assignedSergeant = s;		
		setActive();

		return wasntAssigned;
	}
	
	/**
	 * Enables a mission.
	 */
	public void setActive(){
		this.isActive = true;
	}
	
	/**
	 * Disables the activation of mission.
	 */
	public void disableActive(){
		this.isActive = false;
	}
	
	/**
	 * @return isActive flag
	 */
	public boolean isActive(){
		return this.isActive;
	}
	
	/**
	 * Returns the mission assigned to a sergeant.
	 * @return Sergeant assigned to this mission
	 * @throws IllegalStateException upon a query to unsigned sergeant
	 */
	public Sergeant getAssignedSergeant() throws IllegalStateException{
		if(this.assignedSergeant==null) {
			throw new IllegalStateException();
		}
		return this.assignedSergeant;
	}	
	
	/**
	 * @return True if this mission is assigned to some sergeant
	 */
	public boolean isAssigned() {
		return this.assignedSergeant!=null;
	}
	
	/**
	 * @return the missionName
	 */
	public String getMissionName() {
		return this.missionName;
	}

	/**
	 * @return the requiredSkill
	 */
	public String getRequiredSkill() {
		return this.requiredSkill;
	}

	/**
	 * @return the timeLeft
	 */
	public synchronized int getTimeLeft() {
		return this.timeLeft;
	}

	/**
	 * @param nTimeLeft the timeLeft to set
	 */
	public synchronized void setTimeLeft(int nTimeLeft) {
		this.timeLeft = nTimeLeft;
	}

	/**
	 * @return the itemsRequired
	 */
	public List<ItemRequired> getItemsRequired() {
		return Collections.unmodifiableList(this.itemsRequired);
	}

	/**
	 * @return the preReqMissions
	 */
	public List<Mission> getPreReqMissions() {
		if (null == this.preReqMissions)
			return null;
		return Collections.unmodifiableList(this.preReqMissions);
	}

	/**
	 * @return a repr of the mission
	 */
	public String toString() {
		StringBuilder sb = new StringBuilder();
		Formatter formatter = new Formatter(sb);
		
		return formatter.format("%s (TTL: %d, Active: %b)",
				this.missionName, this.timeLeft, this.isActive).toString();
	}
	
	/**
	 * updates the working hour of a mission.
	 * The function is not synchronized because only one thread will access it. 
	 * @param workHours how much time was it worked.
	 */
	public void updateTimeLeft(int workHours) {
		this.timeLeft -= workHours;
	}

	/**
	 * @param mission adds a mission to the pre-requested missions.
	 */
	public void addPreRequest(Mission mission) {
		this.preReqMissions.add(mission);
	}

	/**
	 * @param reqMissions adds a missions in reqMissions to the pre-requested missions.
	 */
	public void addPreRequest(List<Mission> reqMissions) {
		this.preReqMissions.addAll(reqMissions);		
	}
	
	/**
	 * @return true if predefined missions of this mission are done.
	 */
	public boolean checkPrerequest() {
		// If we don't have missions - we are ok
		if (null == this.preReqMissions)
			return true;
		
		Iterator<Mission> it = this.preReqMissions.iterator();
		Mission currentPreReq;
		while(it.hasNext()){
			currentPreReq = it.next();
			if(currentPreReq.getTimeLeft() != 0)
				return false;
		}
		return true;
	}	

	
	
}
